package com.jinmdz.fmis.api.api.service;

import com.jinmdz.fmis.api.api.model.common.IdVersionData;
import com.jinmdz.fmis.api.api.model.funeral.BusinessCodeData;
import com.jinmdz.fmis.api.api.model.settlement.*;
import com.jinmdz.fmis.api.base.BaseService;
import com.jinmdz.fmis.api.constant.MessageConst;
import com.jinmdz.fmis.api.model.config.dictionary.BusinessLogCode;
import com.jinmdz.fmis.api.model.config.dictionary.DictionaryCode;
import com.jinmdz.fmis.api.model.config.dictionary.OperationLogCode;
import com.jinmdz.fmis.api.model.system.UserItem;
import com.jinmdz.fmis.api.wrapper.DictionaryWrapper;
import com.jinmdz.fmis.api.wrapper.OperationWrapper;
import com.jinmdz.fmis.common.ext.StringBuilderExt;
import com.jinmdz.fmis.common.util.BeanUtil;
import com.jinmdz.fmis.common.util.DataUtil;
import com.jinmdz.fmis.common.util.RenminbiUtil;
import com.jinmdz.fmis.core.base.BaseResult;
import com.jinmdz.fmis.core.exception.ActionException;
import com.jinmdz.fmis.core.util.JacksonUtil;
import com.jinmdz.fmis.dao.dao.ChargeInvoiceDao;
import com.jinmdz.fmis.dao.dao.ChargeInvoiceRedDao;
import com.jinmdz.fmis.dao.dao.ChargeSettleDao;
import com.jinmdz.fmis.dao.dao.FuneralBusinessDao;
import com.jinmdz.fmis.dao.model.funeral.FuneralBusinessViewItem;
import com.jinmdz.fmis.dao.model.settlement.ChargeInvoiceItem;
import com.jinmdz.fmis.dao.model.settlement.ChargeInvoiceRedItem;
import com.jinmdz.fmis.dao.model.settlement.ChargeSettleViewItem;
import com.jinmdz.fmis.mapper.entity.ChargeInvoiceEntity;
import com.jinmdz.fmis.mapper.entity.ChargeInvoiceRedEntity;
import com.jinmdz.fmis.mapper.entity.FuneralBusinessLogEntity;
import com.jinmdz.fmis.mapper.mapper.ChargeInvoiceMapper;
import com.jinmdz.fmis.mapper.mapper.ChargeInvoiceRedMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;

/**
 * ChargeSettlementInvoiceService类
 *
 * @author LiCongLu
 * @date 14:16
 */
@Service("chargeSettlementInvoiceService")
public class ChargeSettlementInvoiceService extends BaseService {

    @Resource
    private FuneralBusinessDao funeralBusinessDao;

    @Resource
    private ChargeSettleDao chargeSettleDao;

    @Resource
    private OperationLogCode operationLogCode;

    @Resource
    private BusinessLogCode businessLogCode;

    @Resource
    private DictionaryCode dictionaryCode;

    @Resource
    private OperationWrapper operationWrapper;

    @Resource
    private ChargeInvoiceDao chargeInvoiceDao;

    @Resource
    private ChargeInvoiceRedDao chargeInvoiceRedDao;

    @Resource
    private ChargeInvoiceMapper chargeInvoiceMapper;

    @Resource
    private HistoryChargeService historyChargeService;

    @Resource
    private DictionaryWrapper dictionaryWrapper;

    @Resource
    private ChargeInvoiceRedMapper chargeInvoiceRedMapper;

    /**
     * 打印发票信息
     *
     * @param userItem 当前账号
     * @param data     打印发票数据
     * @return
     * @author LiCongLu
     * @date 2020-05-13 14:23
     */
    @Transactional(rollbackFor = Exception.class)
    public BaseResult<ChargeInvoiceLoadData> saveChargeSettleToInvoice(UserItem userItem, ChargeInvoiceData data) throws ActionException {
        String businessCode = data.getBusinessCode();
        // 通过业务编码获取殡葬信息
        FuneralBusinessViewItem funeralItem = funeralBusinessDao.getFuneralBusinessViewByBusinessCode(businessCode);
        if (DataUtil.isNull(funeralItem) || DataUtil.isNull(funeralItem.getId(), funeralItem.getVersion())) {
            throw exception(MessageConst.NO_FUNERAL_CODE);
        }

        // 判断是否存在主键
        if (DataUtil.invalid(data.getSettleIds())) {
            throw exception("不存在结算主键");
        }

        ChargeInvoiceItemData invoiceItem = data.getInvoiceItem();
        // 验证发票代码和发票号码
        checkChargeInvoice(invoiceItem);

        // 结算主键处理
        ArrayList<IdVersionData> idList = data.getSettleIds();

        // 验证主键是否重复
        if (idList.size() != getRepeatIdSize(idList)) {
            throw exception("结算主键存在重复");
        }

        //  记录结算费用
        ArrayList<ChargeSettleViewItem> chargeSettleItems = new ArrayList<>();
        ArrayList<Integer> chargeSettleIds = new ArrayList<>();

        // 总结算金额
        BigDecimal totalCharge = new BigDecimal(0);

        // 开始遍历结算
        for (IdVersionData idData : idList) {
            ChargeSettleViewItem settleItem = chargeSettleDao.getChargeSettleViewById(idData.getId());
            if (DataUtil.isNull(settleItem) || DataUtil.isNull(settleItem.getId(), settleItem.getVersion())) {
                throw exception("存在错误结算主键");
            }

            // 验证数据版本号
            if (!DataUtil.equals(settleItem.getVersion(), idData.getVersion())) {
                throw exception(versionError("结算主键"));
            }

            // 验证业务编码
            if (!businessCode.equals(settleItem.getBusinessCode())) {
                throw exception(MessageConst.BUSINESS_CODE_ERROR);
            }

            // 验证是否已打印发票
            if (DataUtil.valid(settleItem.getAsInvoice())) {
                throw exception("存在已打印发票的结算");
            }

            // 记录结算金额
            totalCharge = totalCharge.add(settleItem.getSettleCharge());
            chargeSettleIds.add(settleItem.getId());
            chargeSettleItems.add(settleItem);
        }

        // 判断结算金额
        if (totalCharge.compareTo(new BigDecimal(0)) <= 0) {
            throw exception("打印发票的结算金额必须大于零");
        }

        // 判断结算金额与发票金额
        if (totalCharge.compareTo(data.getInvoiceItem().getInvoiceCharge()) != 0) {
            throw exception("发票金额与结算金额不相等");
        }

        // 打印发票信息
        return saveChargeSettleToInvoice(userItem, funeralItem, data, chargeSettleItems, chargeSettleIds);
    }

    /**
     * 验证发票相关信息
     *
     * @param invoiceItem 发票信息
     * @return
     * @author LiCongLu
     * @date 2020-05-14 11:26
     */
    private void checkChargeInvoice(ChargeInvoiceItemData invoiceItem) throws ActionException {
        // 验证发票代码
        Integer invoiceCodeId = chargeInvoiceDao.getChargeInvoiceIdByInvoiceCode(invoiceItem.getInvoiceCode());
        if (DataUtil.valid(invoiceCodeId)) {
            throw exception("存在相同的发票代码");
        }

        // 验证红票发票代码
        Integer invoiceRedCodeId = chargeInvoiceRedDao.getChargeInvoiceRedIdByInvoiceCode(invoiceItem.getInvoiceCode());
        if (DataUtil.valid(invoiceRedCodeId)) {
            throw exception("存在相同的发票代码");
        }

        // 验证发票号码
        Integer invoiceNoId = chargeInvoiceDao.getChargeInvoiceIdByInvoiceNo(invoiceItem.getInvoiceNo());
        if (DataUtil.valid(invoiceNoId)) {
            throw exception("存在相同的发票号码");
        }

        // 验证发票号码
        Integer invoiceRedNoId = chargeInvoiceRedDao.getChargeInvoiceRedIdByInvoiceNo(invoiceItem.getInvoiceNo());
        if (DataUtil.valid(invoiceRedNoId)) {
            throw exception("存在相同的发票号码");
        }
    }

    /**
     * 保存打印发票
     *
     * @param userItem          当前账号
     * @param funeralItem       殡葬业务
     * @param data              发票数据
     * @param chargeSettleItems 要打印发票的结算集合
     * @param chargeSettleIds   要打印发票的结算主键
     * @return
     * @author LiCongLu
     * @date 2020-05-13 14:43
     */
    private BaseResult<ChargeInvoiceLoadData> saveChargeSettleToInvoice(UserItem userItem, FuneralBusinessViewItem funeralItem, ChargeInvoiceData data, ArrayList<ChargeSettleViewItem> chargeSettleItems, ArrayList<Integer> chargeSettleIds) throws ActionException {
        String businessCode = data.getBusinessCode();
        ChargeInvoiceItemData invoiceItem = data.getInvoiceItem();

        // 记录发票信息
        ChargeInvoiceEntity entity = getEntity(userItem, ChargeInvoiceEntity.class);
        BeanUtil.copy2Bean(invoiceItem, entity);
        // 设置发票信息
        entity.setBusinessCode(businessCode)
                .setInvoiceStateCode(dictionaryCode.getInvoiceStateDaYin())
                .setInvoiceTime(nowDate());
        // 添加打印发票信息
        chargeInvoiceMapper.insertChargeInvoice(entity);
        if (DataUtil.invalid(entity.getId())) {
            throw exception("保存发票信息失败");
        }

        // 更新结算发票记录
        chargeSettleDao.updateForInvoice(businessCode, chargeSettleIds, entity.getId(), userItem.getId());

        // 保存历史记录发票信息
        historyChargeService.saveHistoryCharge(userItem, funeralItem, entity.getInvoiceNo(), chargeSettleIds);

        // 返回打印发票结果
        return loadPrintChargeInvoice(entity);
    }

    /**
     * 打印发票日志
     *
     * @param userItem          当前账号
     * @param funeralItem       殡葬业务
     * @param data              发票请求数据
     * @param chargeSettleItems 发票结算信息
     * @param entity            打印发票实体
     * @return
     * @author LiCongLu
     * @date 2020-05-14 08:48
     */
    private void saveChargeSettleToInvoiceLog(UserItem userItem, FuneralBusinessViewItem funeralItem, ChargeInvoiceData data, ArrayList<ChargeSettleViewItem> chargeSettleItems, ChargeInvoiceEntity entity) throws ActionException {
        // 添加业务日志及操作日志
        String businessCode = funeralItem.getBusinessCode();
        Integer typeCode = businessLogCode.getChargeInvoiceDaYin();
        FuneralBusinessLogEntity businessLogEntity = operationWrapper.businessLog(userItem, businessCode, typeCode);

        // 操作日志字符串
        StringBuilderExt builder = new StringBuilderExt();
        builder.format("发票打印操作，业务编码[{0}]，逝者姓名[{1}]，发票代码[{2}]，发票号码[{3}]，发票抬头[{4}]，发票金额[{5}]，操作人员[{6}]"
                , businessCode, funeralItem.getDeadName(), entity.getInvoiceCode(), entity.getInvoiceNo()
                , entity.getInvoiceTitle(), DataUtil.getPlainString(entity.getInvoiceCharge()), userItem.getFullName());

        // 添加日志
        operationWrapper.operationLog(userItem, businessLogEntity, operationLogCode.getChargeInvoiceDaYin(), builder.toString()
                , JacksonUtil.obj2Json(data), JacksonUtil.obj2Json(chargeSettleItems), JacksonUtil.obj2Json(entity));
    }

    /**
     * 打印发票或重打发票加载数据
     *
     * @param entity 打印发票实体
     * @return
     * @author LiCongLu
     * @date 2020-05-13 16:23
     */
    private BaseResult<ChargeInvoiceLoadData> loadPrintChargeInvoice(ChargeInvoiceEntity entity) {
        ChargeInvoiceLoadData loadData = new ChargeInvoiceLoadData();
        BeanUtil.copy2Bean(entity, loadData);
        // 设置发票金额大写
        loadData.setInvoiceChargeText(RenminbiUtil.toRmb(loadData.getInvoiceCharge()));
        return successData(loadData);
    }

    /**
     * 根据funeral_dead表中businessCode加载逝者发票管理所有信息
     *
     * @param data 业务编码
     * @return
     * @author LiCongLu
     * @date 2020-05-14 10:59
     */
    public BaseResult<ChargeInvoiceAllLoadData> loadChargeInvoiceAll(BusinessCodeData data) {
        String businessCode = data.getBusinessCode();
        // 通过业务编码获取殡葬信息
        FuneralBusinessViewItem funeralItem = funeralBusinessDao.getFuneralBusinessViewByBusinessCode(businessCode);
        if (DataUtil.isNull(funeralItem) || DataUtil.isNull(funeralItem.getId(), funeralItem.getVersion())) {
            return failure(MessageConst.NO_FUNERAL_CODE);
        }

        // 创建响应实体
        ChargeInvoiceAllLoadData loadData = new ChargeInvoiceAllLoadData();

        // 查询发票信息
        ArrayList<ChargeInvoiceItem> invoiceItems = chargeInvoiceDao.listChargeInvoiceByBusinessCode(businessCode);
        ArrayList<ChargeInvoiceListItem> invoiceListItems = BeanUtil.copy2List(invoiceItems, ChargeInvoiceListItem.class);

        // 设置字典数据
        dictionaryWrapper.resetDataText(invoiceListItems);

        // 查询红票信息
        ArrayList<ChargeInvoiceRedItem> invoiceRedItems = chargeInvoiceRedDao.listChargeInvoiceRedByBusinessCode(businessCode);
        ArrayList<ChargeInvoiceRedListItem> invoiceRedListItems = BeanUtil.copy2List(invoiceRedItems, ChargeInvoiceRedListItem.class);
        // 设置字典数据
        dictionaryWrapper.resetDataText(invoiceRedListItems);

        // 判断是否可重打状态
        for (ChargeInvoiceListItem listItem : invoiceListItems) {
            Integer reprint = DataUtil.equals(listItem.getInvoiceStateCode(), dictionaryCode.getInvoiceStateZuoFei()) ? 0 : 1;
            listItem.setAsReprint(reprint);
        }

        // 判断是否可重打状态
        for (ChargeInvoiceRedListItem listItem : invoiceRedListItems) {
            listItem.setAsReprint(1);
        }

        // 设置返回数据
        loadData.setInvoiceItems(invoiceListItems)
                .setInvoiceRedItems(invoiceRedListItems);

        return successData(loadData);
    }

    /**
     * 重打发票接口
     *
     * @param data 发票主键和版本号
     * @return
     * @author LiCongLu
     * @date 2020-05-14 11:33
     */
    @Transactional(rollbackFor = Exception.class)
    public BaseResult<ChargeInvoiceLoadData> saveChargeInvoiceToReprint(UserItem userItem, ChargeInvoiceIdData data) throws ActionException {
        String businessCode = data.getBusinessCode();
        // 通过业务编码获取殡葬信息
        FuneralBusinessViewItem funeralItem = funeralBusinessDao.getFuneralBusinessViewByBusinessCode(businessCode);
        if (DataUtil.isNull(funeralItem) || DataUtil.isNull(funeralItem.getId(), funeralItem.getVersion())) {
            throw exception(MessageConst.NO_FUNERAL_CODE);
        }

        // 获取打印发票
        IdVersionData idData = data.getInvoiceId();
        // 验证主键和数据版本是否存在
        if (DataUtil.isNull(idData.getId(), idData.getVersion())) {
            throw exception("请求发票信息主键与版本号不能为空");
        }

        // 通过主键获取发票信息
        ChargeInvoiceEntity entity = chargeInvoiceMapper.getChargeInvoiceById(idData.getId());
        if (DataUtil.isNull(entity) || DataUtil.isNull(entity.getId(), entity.getVersion())) {
            throw exception("发票主键错误，不存在此发票信息");
        }

        // 验证业务编码
        if (!data.getBusinessCode().equals(entity.getBusinessCode())) {
            throw exception(MessageConst.BUSINESS_CODE_ERROR);
        }

        // 验证发票信息版本
        if (!DataUtil.equals(idData.getVersion(), entity.getVersion())) {
            throw exception(versionError("发票信息"));
        }

        // 判断发票是否已作废
        if (DataUtil.equals(entity.getInvoiceStateCode(), dictionaryCode.getInvoiceStateZuoFei())) {
            throw exception("已作废发票不可继续重打");
        }

        String oldValue = JacksonUtil.obj2Json(entity);

        // 设置重打信息
        entity.setInvoiceTime(nowDate())
                .setInvoiceStateCode(dictionaryCode.getInvoiceStateChongDa());
        chargeInvoiceMapper.updateChargeInvoice(entity);

        // 添加重打操作日志
        saveChargeInvoiceToReprintLog(userItem, funeralItem, data, oldValue, entity);

        return loadPrintChargeInvoice(entity);
    }

    /**
     * 添加重打发票日志记录
     *
     * @param userItem    当前账号
     * @param funeralItem 殡葬业务
     * @param data        请求数据
     * @param oldValue    旧数据
     * @param entity      新实体
     * @return
     * @author LiCongLu
     * @date 2020-05-14 11:53
     */
    private void saveChargeInvoiceToReprintLog(UserItem userItem, FuneralBusinessViewItem funeralItem, ChargeInvoiceIdData data, String oldValue, ChargeInvoiceEntity entity) throws ActionException {
        // 添加业务日志及操作日志
        String businessCode = funeralItem.getBusinessCode();
        Integer typeCode = businessLogCode.getChargeInvoiceChongDa();
        FuneralBusinessLogEntity businessLogEntity = operationWrapper.businessLog(userItem, businessCode, typeCode);

        // 操作日志字符串
        StringBuilderExt builder = new StringBuilderExt();
        builder.format("发票重打操作，业务编码[{0}]，逝者姓名[{1}]，发票代码[{2}]，发票号码[{3}]，发票抬头[{4}]，发票金额[{5}]，操作人员[{6}]"
                , businessCode, funeralItem.getDeadName(), entity.getInvoiceCode(), entity.getInvoiceNo()
                , entity.getInvoiceTitle(), DataUtil.getPlainString(entity.getInvoiceCharge()), userItem.getFullName());

        // 添加日志
        operationWrapper.operationLog(userItem, businessLogEntity, operationLogCode.getChargeInvoiceChongDa(), builder.toString()
                , JacksonUtil.obj2Json(data), oldValue, JacksonUtil.obj2Json(entity));
    }

    /**
     * 作废发票
     *
     * @param userItem 当前账号
     * @param data     发票主键和数据版本号
     * @return
     * @author LiCongLu
     * @date 2020-05-14 13:12
     */
    @Transactional(rollbackFor = Exception.class)
    public BaseResult saveChargeInvoiceToCancellation(UserItem userItem, ChargeInvoiceIdData data) throws ActionException {
        String businessCode = data.getBusinessCode();
        // 通过业务编码获取殡葬信息
        FuneralBusinessViewItem funeralItem = funeralBusinessDao.getFuneralBusinessViewByBusinessCode(businessCode);
        if (DataUtil.isNull(funeralItem) || DataUtil.isNull(funeralItem.getId(), funeralItem.getVersion())) {
            throw exception(MessageConst.NO_FUNERAL_CODE);
        }

        // 获取打印发票
        IdVersionData idData = data.getInvoiceId();
        // 验证主键和数据版本是否存在
        if (DataUtil.isNull(idData.getId(), idData.getVersion())) {
            throw exception("请求发票信息主键与版本号不能为空");
        }

        // 通过主键获取发票信息
        ChargeInvoiceEntity entity = chargeInvoiceMapper.getChargeInvoiceById(idData.getId());
        if (DataUtil.isNull(entity) || DataUtil.isNull(entity.getId(), entity.getVersion())) {
            throw exception("发票主键错误，不存在此发票信息");
        }

        // 验证业务编码
        if (!data.getBusinessCode().equals(entity.getBusinessCode())) {
            throw exception(MessageConst.BUSINESS_CODE_ERROR);
        }

        // 验证发票信息版本
        if (!DataUtil.equals(idData.getVersion(), entity.getVersion())) {
            throw exception(versionError("发票信息"));
        }

        // 判断发票是否已作废
        if (DataUtil.equals(entity.getInvoiceStateCode(), dictionaryCode.getInvoiceStateZuoFei())) {
            throw exception("已作废发票不可继续作废");
        }

        String oldValue = JacksonUtil.obj2Json(entity);

        // 设置作废信息
        entity.setInvoiceStateCode(dictionaryCode.getInvoiceStateZuoFei());
        chargeInvoiceMapper.updateChargeInvoice(entity);

        // 更新结算作废发票状态
        chargeSettleDao.updateForInvoiceCancellation(businessCode, entity.getId(), userItem.getId());

        // 添加重打操作日志
        saveChargeInvoiceToCancellationLog(userItem, funeralItem, data, oldValue, entity);

        return success("作废成功");
    }


    /**
     * 添加重打发票日志记录
     *
     * @param userItem    当前账号
     * @param funeralItem 殡葬业务
     * @param data        请求数据
     * @param oldValue    旧数据
     * @param entity      新实体
     * @return
     * @author LiCongLu
     * @date 2020-05-14 13:19
     */
    private void saveChargeInvoiceToCancellationLog(UserItem userItem, FuneralBusinessViewItem funeralItem, ChargeInvoiceIdData data, String oldValue, ChargeInvoiceEntity entity) throws ActionException {
        // 添加业务日志及操作日志
        String businessCode = funeralItem.getBusinessCode();
        Integer typeCode = businessLogCode.getChargeInvoiceZuoFei();
        FuneralBusinessLogEntity businessLogEntity = operationWrapper.businessLog(userItem, businessCode, typeCode);

        // 操作日志字符串
        StringBuilderExt builder = new StringBuilderExt();
        builder.format("发票作废操作，业务编码[{0}]，逝者姓名[{1}]，发票代码[{2}]，发票号码[{3}]，发票抬头[{4}]，发票金额[{5}]，操作人员[{6}]"
                , businessCode, funeralItem.getDeadName(), entity.getInvoiceCode(), entity.getInvoiceNo()
                , entity.getInvoiceTitle(), DataUtil.getPlainString(entity.getInvoiceCharge()), userItem.getFullName());

        // 添加日志
        operationWrapper.operationLog(userItem, businessLogEntity, operationLogCode.getChargeInvoiceZuoFei(), builder.toString()
                , JacksonUtil.obj2Json(data), oldValue, JacksonUtil.obj2Json(entity));
    }

    /**
     * 冲红发票信息
     *
     * @param userItem 当前账号
     * @param data     冲红新发票信息
     * @return
     * @author LiCongLu
     * @date 2020-05-14 13:45
     */
    @Transactional(rollbackFor = Exception.class)
    public BaseResult<ChargeInvoiceLoadData> saveChargeInvoiceToRed(UserItem userItem, ChargeInvoiceRedData data) throws ActionException {
        String businessCode = data.getBusinessCode();
        // 通过业务编码获取殡葬信息
        FuneralBusinessViewItem funeralItem = funeralBusinessDao.getFuneralBusinessViewByBusinessCode(businessCode);
        if (DataUtil.isNull(funeralItem) || DataUtil.isNull(funeralItem.getId(), funeralItem.getVersion())) {
            throw exception(MessageConst.NO_FUNERAL_CODE);
        }

        // 获取打印发票
        IdVersionData idData = data.getInvoiceId();
        // 验证主键和数据版本是否存在
        if (DataUtil.isNull(idData.getId(), idData.getVersion())) {
            throw exception("请求发票信息主键与版本号不能为空");
        }

        // 通过主键获取发票信息
        ChargeInvoiceEntity entity = chargeInvoiceMapper.getChargeInvoiceById(idData.getId());
        if (DataUtil.isNull(entity) || DataUtil.isNull(entity.getId(), entity.getVersion())) {
            throw exception("发票主键错误，不存在此发票信息");
        }

        // 验证业务编码
        if (!data.getBusinessCode().equals(entity.getBusinessCode())) {
            throw exception(MessageConst.BUSINESS_CODE_ERROR);
        }

        // 验证发票信息版本
        if (!DataUtil.equals(idData.getVersion(), entity.getVersion())) {
            throw exception(versionError("发票信息"));
        }

        // 判断发票是否已作废
        if (DataUtil.equals(entity.getInvoiceStateCode(), dictionaryCode.getInvoiceStateZuoFei())) {
            throw exception("已作废发票不可冲红");
        }

        String oldValue = JacksonUtil.obj2Json(entity);

        // 设置作废信息
        entity.setInvoiceStateCode(dictionaryCode.getInvoiceStateZuoFei());
        chargeInvoiceMapper.updateChargeInvoice(entity);

        // 更新结算作废发票状态
        chargeSettleDao.updateForInvoiceCancellation(businessCode, entity.getId(), userItem.getId());

        ChargeInvoiceItemData invoiceItem = data.getInvoiceRedItem();
        // 验证发票代码和发票号码
        checkChargeInvoice(invoiceItem);

        // 判断冲红金额与发票金额
        if (entity.getInvoiceCharge().compareTo(invoiceItem.getInvoiceCharge()) != 0) {
            throw exception("冲红发票金与发票金额不相等");
        }

        // 记录发票冲红信息
        ChargeInvoiceRedEntity redEntity = getEntity(userItem, ChargeInvoiceRedEntity.class);
        BeanUtil.copy2Bean(invoiceItem, redEntity);
        // 设置发票冲红信息
        redEntity.setBusinessCode(businessCode)
                .setInvoiceStateCode(dictionaryCode.getInvoiceStateDaYin())
                .setInvoiceTime(nowDate());
        // 设置被冲红发票编号及红票金额负值
        redEntity.setInvoiceCancelNo(entity.getInvoiceNo())
                .setInvoiceCharge(new BigDecimal(0).subtract(entity.getInvoiceCharge()));

        // 添加打印发票冲红信息
        chargeInvoiceRedMapper.insertChargeInvoiceRed(redEntity);
        if (DataUtil.invalid(redEntity.getId())) {
            throw exception("保存冲红发票信息失败");
        }
        // 添加冲红发票日志
        saveChargeInvoiceToRedLog(userItem, funeralItem, data, oldValue, redEntity);

        return loadPrintChargeInvoiceRed(redEntity);
    }

    /**
     * 添加冲红操作日志记录
     *
     * @param userItem    当前账号
     * @param funeralItem 殡葬业务
     * @param data        请求数据
     * @param oldValue    旧数据
     * @param entity      新实体
     * @return
     * @author LiCongLu
     * @date 2020-05-14 13:56
     */
    private void saveChargeInvoiceToRedLog(UserItem userItem, FuneralBusinessViewItem funeralItem, ChargeInvoiceRedData data, String oldValue, ChargeInvoiceRedEntity entity) throws ActionException {
        // 添加业务日志及操作日志
        String businessCode = funeralItem.getBusinessCode();
        Integer typeCode = businessLogCode.getChargeInvoiceChongHong();
        FuneralBusinessLogEntity businessLogEntity = operationWrapper.businessLog(userItem, businessCode, typeCode);

        // 操作日志字符串
        StringBuilderExt builder = new StringBuilderExt();
        builder.format("发票冲红操作，业务编码[{0}]，逝者姓名[{1}]，发票代码[{2}]，发票号码[{3}]，发票抬头[{4}]，红票金额[{5}]，操作人员[{6}]"
                , businessCode, funeralItem.getDeadName(), entity.getInvoiceCode(), entity.getInvoiceNo()
                , entity.getInvoiceTitle(), DataUtil.getPlainString(entity.getInvoiceCharge()), userItem.getFullName());

        // 添加日志
        operationWrapper.operationLog(userItem, businessLogEntity, operationLogCode.getChargeInvoiceChongHong(), builder.toString()
                , JacksonUtil.obj2Json(data), oldValue, JacksonUtil.obj2Json(entity));
    }

    /**
     * 冲红发票或重打红发票加载数据
     *
     * @param entity 打印红发票实体
     * @return
     * @author LiCongLu
     * @date 2020-05-14 13:58
     */
    private BaseResult<ChargeInvoiceLoadData> loadPrintChargeInvoiceRed(ChargeInvoiceRedEntity entity) {
        ChargeInvoiceLoadData loadData = new ChargeInvoiceLoadData();
        BeanUtil.copy2Bean(entity, loadData);
        // 设置发票金额大写
        loadData.setInvoiceChargeText("负" + RenminbiUtil.toRmb(new BigDecimal(0).subtract(loadData.getInvoiceCharge())));
        return successData(loadData);
    }

    /**
     * 重打红票发票接口
     *
     * @param data 红票发票主键和版本号
     * @return
     * @author LiCongLu
     * @date 2020-05-14 14:12
     */
    @Transactional(rollbackFor = Exception.class)
    public BaseResult<ChargeInvoiceLoadData> saveChargeInvoiceRedToReprint(UserItem userItem, ChargeInvoiceIdData data) throws ActionException {
        String businessCode = data.getBusinessCode();
        // 通过业务编码获取殡葬信息
        FuneralBusinessViewItem funeralItem = funeralBusinessDao.getFuneralBusinessViewByBusinessCode(businessCode);
        if (DataUtil.isNull(funeralItem) || DataUtil.isNull(funeralItem.getId(), funeralItem.getVersion())) {
            throw exception(MessageConst.NO_FUNERAL_CODE);
        }

        // 获取打印红票发票
        IdVersionData idData = data.getInvoiceId();
        // 验证主键和数据版本是否存在
        if (DataUtil.isNull(idData.getId(), idData.getVersion())) {
            throw exception("请求红票发票信息主键与版本号不能为空");
        }

        // 通过主键获取红票发票信息
        ChargeInvoiceRedEntity entity = chargeInvoiceRedMapper.getChargeInvoiceRedById(idData.getId());
        if (DataUtil.isNull(entity) || DataUtil.isNull(entity.getId(), entity.getVersion())) {
            throw exception("红票发票主键错误，不存在此红票发票信息");
        }

        // 验证业务编码
        if (!data.getBusinessCode().equals(entity.getBusinessCode())) {
            throw exception(MessageConst.BUSINESS_CODE_ERROR);
        }

        // 验证红票发票信息版本
        if (!DataUtil.equals(idData.getVersion(), entity.getVersion())) {
            throw exception(versionError("红票发票信息"));
        }

        String oldValue = JacksonUtil.obj2Json(entity);

        // 设置重打信息
        entity.setInvoiceTime(nowDate())
                .setInvoiceStateCode(dictionaryCode.getInvoiceStateChongDa());
        chargeInvoiceRedMapper.updateChargeInvoiceRed(entity);

        // 添加重打操作日志
        saveChargeInvoiceRedToReprintLog(userItem, funeralItem, data, oldValue, entity);

        return loadPrintChargeInvoiceRed(entity);
    }

    /**
     * 添加重打红票发票日志记录
     *
     * @param userItem    当前账号
     * @param funeralItem 殡葬业务
     * @param data        请求数据
     * @param oldValue    旧数据
     * @param entity      新实体
     * @return
     * @author LiCongLu
     * @date 2020-05-14 14:14
     */
    private void saveChargeInvoiceRedToReprintLog(UserItem userItem, FuneralBusinessViewItem funeralItem, ChargeInvoiceIdData data, String oldValue, ChargeInvoiceRedEntity entity) throws ActionException {
        // 添加业务日志及操作日志
        String businessCode = funeralItem.getBusinessCode();
        Integer typeCode = businessLogCode.getChargeInvoiceChongDa();
        FuneralBusinessLogEntity businessLogEntity = operationWrapper.businessLog(userItem, businessCode, typeCode);

        // 操作日志字符串
        StringBuilderExt builder = new StringBuilderExt();
        builder.format("发票红票重打操作，业务编码[{0}]，逝者姓名[{1}]，发票代码[{2}]，发票号码[{3}]，发票抬头[{4}]，发票金额[{5}]，操作人员[{6}]"
                , businessCode, funeralItem.getDeadName(), entity.getInvoiceCode(), entity.getInvoiceNo()
                , entity.getInvoiceTitle(), DataUtil.getPlainString(entity.getInvoiceCharge()), userItem.getFullName());

        // 添加日志
        operationWrapper.operationLog(userItem, businessLogEntity, operationLogCode.getChargeInvoiceChongDa(), builder.toString()
                , JacksonUtil.obj2Json(data), oldValue, JacksonUtil.obj2Json(entity));
    }
}
